package cn.hiapi.core.basic.sharding;

import cn.hiapi.core.basic.context.DispatchContext;
import cn.hiapi.core.basic.sharding.algorithm.ShardingAlgorithmTable;
import cn.hiapi.core.basic.sharding.config.ShardingTableScan;
import cn.hiapi.core.basic.sharding.entity.BasicShardingEntity;
import cn.hiapi.core.basic.sharding.entity.ShardingOption;
import cn.hiapi.core.basic.sharding.enums.ShardingMode;
import cn.hiapi.core.basic.sharding.properties.ShardingTimeProperties;
import cn.hiapi.core.basic.sharding.utils.DatabaseUtils;
import cn.hiapi.core.basic.sharding.utils.ShardingUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;
import java.util.Date;
import java.util.List;
import java.util.Set;

/**
 * @author AdinZ
 * @date 2024/1/12 16:57
 * 自动分表任务
 */
@Component
@Slf4j
public class AutoShardingTableTask {

    private final DispatchContext dispatchContext;

    public AutoShardingTableTask(DispatchContext dispatchContext) {
        this.dispatchContext = dispatchContext;
    }

    /**
     * 每天0点生成一次分表
     */
    @Scheduled(cron = "0 0 0 * * ? ")
    public void task() {

        log.info("[分表任务] 开始执行分表任务");
        final DataSource dataSource = this.dispatchContext.getServiceOne(DataSource.class);

        try {
            ShardingTableScan tableScan = new ShardingTableScan(BasicShardingEntity.class, "cn/hiapi");
            List<Class<?>> tablesClass = tableScan.getTableClass();
            tablesClass.forEach(tableClass -> {
                try {
                    BasicShardingEntity instance = ShardingTableScan.newInstance(tableClass);
                    ShardingOption option = instance.getOption();
                    if (option.getMode() == ShardingMode.TIME) {
                        ShardingTimeProperties properties = (ShardingTimeProperties) option.getProperties();
                        String tableCurrent = ShardingUtils.getTableTime(option.getTable(), System.currentTimeMillis(), properties.getFormat());
                        String tableNext = ShardingUtils.getTableTime(option.getTable(), DateUtils.addMonths(new Date(), 1).getTime(), properties.getFormat());
                        Set<String> queryTables = DatabaseUtils.queryTables(dataSource, option.getTable());

                        createTable(dataSource, queryTables, option.getTable(), tableCurrent);
                        createTable(dataSource, queryTables, option.getTable(), tableNext);
                    }
                } catch (Exception e) {
                    log.error("[分表任务]  创建分表出现错误 类:{}", tableClass.getSimpleName(), e);
                }
            });
        } catch (Exception e) {
            log.error("[分表任务]  执行分表任务出现错误", e);
        }
        log.info("[分表任务] 结束分表任务");
    }

    private void createTable(DataSource dataSource, Set<String> queryTables, String logicTable, String createTable) {
        if (queryTables.contains(createTable)) {
            log.info("[分表任务] 逻辑表{} 分表:{} 已经存在 无需处理", logicTable, createTable);
            return;
        }
        log.info("[分表任务] 逻辑表{} 分表:{} 不存在,立即创建", logicTable, createTable);
        try {
            DatabaseUtils.createTable(dataSource, logicTable, createTable);
            //创建成功后, 表名存入 变量中
            ShardingAlgorithmTable.TABLES.add(createTable);
        } catch (Exception e) {
            log.error("[分表任务] 逻辑表{} 分表:{} 创建表失败 ", logicTable, createTable, e);
        }
    }


}
